﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace BoneImp {
	public enum AtlasFormat{
		ALPHA, INTENSITY, LUMINANCE_ALPHA, RGB565, RGBA4444, RGB888, RGBA8888
	};

	public enum AtlasFilter{
		NEAREST,
		LINEAR,
		MIPMAP,
		MIPMAP_NEAREST_NEAREST,
		MIPMAP_LINEAR_NEAREST,
		MIPMAP_NEAREST_LINEAR,
		MIPMAP_LINEAR_LINEAR
	};

	public enum AtlasWrap{
		ATLAS_MIRROREDREPEAT, ATLAS_CLAMPTOEDGE, ATLAS_REPEAT
	};

	public class Atlas {
		AtlasPage pages;
		public AtlasRegion regions;


		static string ReadValue(string line){
			return line.Split(new char[]{':'})[1].Trim();
		}

		static string ReadKey(string line){
			return line.Split(new char[]{':'})[0].Trim();
		}

		static string[] ReadTuple(string line){
			string value = ReadValue(line);
			string[] values = value.Split(new char[]{','});
			for (int i = 0; i < values.Length; i++){
				values[i] = values[i].Trim();
			}
			return values;
		}

		/* Image files referenced in the atlas file will be prefixed dir. */
		static Atlas readAtlas(StreamReader file, string dir) {
			int dirLength = dir.Length;
			bool needsSlash = dirLength > 0 && dir[dirLength - 1] != '/' && dir[dirLength - 1] != '\\';

			Atlas ret = new Atlas();

			AtlasPage page = null;
			AtlasPage lastPage = null;
			AtlasRegion lastRegion = null;
			string str;
			string[] tuple;
			while (!file.EndOfStream){
				str = file.ReadLine();
				if (str.Length == 0) {
					page = null;
				} else if (page == null) {
					string name = str;
					
					string path = dir;				
					if (needsSlash) path += '/';
					path += name;

					//Todo: Figure this out
					//page = new AtlasPage(name, path);
					page = new AtlasPage(path);
					if (lastPage != null)
						lastPage.next = page;
					else
						ret.pages = page;
					lastPage = page;

					str = ReadValue(file.ReadLine());
					Enum.TryParse<AtlasFormat>(str.ToUpper(), out page.format);

					tuple = ReadTuple(file.ReadLine());
					Enum.TryParse<AtlasFilter>(tuple[0].ToUpper(), out page.minFilter);
					Enum.TryParse<AtlasFilter>(tuple[1].ToUpper(), out page.magFilter);

					str = ReadValue(file.ReadLine());
					if (str != "none") {
						page.uWrap = str == "x" ? AtlasWrap.ATLAS_REPEAT : (str == "y" ? AtlasWrap.ATLAS_CLAMPTOEDGE : AtlasWrap.ATLAS_REPEAT);
						page.vWrap = str == "x" ? AtlasWrap.ATLAS_CLAMPTOEDGE : (str == "y" ? AtlasWrap.ATLAS_REPEAT : AtlasWrap.ATLAS_REPEAT);
					}
				} else {
					AtlasRegion region = new AtlasRegion();
					region.texture = page.texture;
					if (lastRegion != null)
						lastRegion.next = region;
					else
						ret.regions = region;
					lastRegion = region;

					region.page = page;
					region.name = str;

					str = ReadValue(file.ReadLine());
					region.rotate = (str.ToLower() == "true");

					tuple = ReadTuple(file.ReadLine());
					region.x = Int32.Parse(tuple[0]);
					region.y = Int32.Parse(tuple[1]);

					tuple = ReadTuple(file.ReadLine());
					region.width = Int32.Parse(tuple[0]);
					region.height = Int32.Parse(tuple[1]);

					str = file.ReadLine();
					tuple = ReadTuple(str);
					if (ReadKey(str) == "split") { /* split is optional */
						region.splits = new int[4];
						region.splits[0] = Int32.Parse(tuple[0]);
						region.splits[1] = Int32.Parse(tuple[1]);
						region.splits[2] = Int32.Parse(tuple[2]);
						region.splits[3] = Int32.Parse(tuple[3]);
						
						str = file.ReadLine();
						tuple = ReadTuple(str);
						if (ReadKey(str) == "pad") { /* pad is optional, but only present with splits */
							region.pads = new int[4];
							region.pads[0] = Int32.Parse(tuple[0]);
							region.pads[1] = Int32.Parse(tuple[1]);
							region.pads[2] = Int32.Parse(tuple[2]);
							region.pads[3] = Int32.Parse(tuple[3]);

							str = file.ReadLine();
							tuple = ReadTuple(str);
						}
					}

					region.originalWidth = Int32.Parse(tuple[0]);
					region.originalHeight = Int32.Parse(tuple[1]);

					tuple = ReadTuple(file.ReadLine());
					region.offsetX = float.Parse(tuple[0]);
					region.offsetY = float.Parse(tuple[1]);

					str = ReadValue(file.ReadLine());
					region.index = Int32.Parse(str);
				}
			}
			return ret;
		}

		/* Image files referenced in the atlas file will be prefixed with the directory containing the atlas file. */
		public static Atlas readAtlasFile(string path) {
			Atlas atlas = null;

			// Get directory from atlas path.
			int lastSlash = path.LastIndexOfAny(new char[] {'\\', '/'});
			if (lastSlash == 0) lastSlash++; // Never drop starting slash.
			int dirLength = (lastSlash != 0) ? lastSlash : 0;
			//char* dir = MALLOC(char, dirLength + 1);
			string dir = path.Substring(0, dirLength);

			StreamReader sr = new StreamReader(path);
			if (sr != null) atlas = readAtlas(sr, dir);

			return atlas;
		}

		/* Returns 0 if the region was not found. */
		public AtlasRegion findRegion(string name) {
			AtlasRegion region = this.regions;
			while (region != null) {
				if (region.name == name) return region;
				region = region.next;
			}
			return null;
		}

	}
}
